<?php

class Player {

    private $name;
    private $gender;
    private $resources;
    private $piles;
    private $phase;
    private $room;
    private $numberOfActionsPlayed;
    private $numberOfTreasuresPlayed;
    private $numberOfTurns;
    private $numberOfCardsBought;
    private $undoStack;
    private $possessedTurns;

    public function __construct($name, $gender = 'male', $piles = null, $resources = null, $phase = null, $room = 0, $numberOfActionsPlayed = 0, $numberOfTreasuresPlayed = 0, $numberOfTurns = 0, $numberOfCardsBought = 0, $undoStack = array(), $possessedTurns = 0) {
        $this->name = $name;
        $this->gender = $gender;
        if ($resources)
            $this->resources = $resources;
        else {
            $this->resources = new Resources();
        }
        if ($piles)
            $this->piles = $piles;
        else {
            $this->piles = new Piles ();
            //$this->drawCards(5);
        }

        if ($phase) {
            $this->phase = $phase;
        } else {
            $this->phase = Phase_Action;
        }
        $this->room = $room;

        $this->numberOfActionsPlayed = $numberOfActionsPlayed;
        $this->numberOfTreasuresPlayed = $numberOfTreasuresPlayed;
        $this->numberOfTurns = $numberOfTurns;
        $this->numberOfCardsBought = $numberOfCardsBought;
        $this->undoStack = $undoStack;
        $this->possessedTurns = $possessedTurns;
    }

    public function setDeckWithDarkAges() {
        $this->piles->setDeckWithDarkAges();
    }

    public function setDeck() {
        $this->piles->setDeck();
    }

    public function setDeckForTest() {
        $this->piles->setDeckForTest();
    }

    public function startGame($game) {
        $printMessage = false;
        $drawed_cards = $this->drawCards($game, 5, $printMessage);
        ScreenDisplay::addScreenMessage($game, true, "draw " . nicePrint($drawed_cards), "draws " . sizeof($drawed_cards) . " cards", $this->name);
    }

    public function getPlayerData($game) {
        $data = array();
        $data['name'] = $this->name;
        $data['phase'] = $this->phase;
        //$data['victorypoints'] = $this->getVictoryPoints($game);
        $data['numberofturns'] = $this->numberOfTurns;
        $validUndo = true;
        if (sizeof($this->undoStack) == 0) {
            $validUndo = false;
        } else {
            $item = $this->undoStack[sizeof($this->undoStack) - 1];
            $validUndo = $item[2];
        }
        $data['validundo'] = $validUndo;
        $data['cointokens'] = $this->getCoinTokens();
        $data = $this->piles->getPilesData($game, $data);
        return $data;
    }

    public function getPublicPlayerData($game) {
        $data = array();
        $data['name'] = $this->name;
        $data['phase'] = $this->phase;
        $data['actions'] = $this->getActions();
        $data['buys'] = $this->getBuys();
        $data['coins'] = $this->getMoney()->getCoins();
        $data['potions'] = $this->getMoney()->getPotions();
        $data['pirateshiptokens'] = $this->getPirateShipTokens();
        $data['cointokens'] = $this->getCoinTokens();
        $data['victorytokens'] = $this->getVictoryTokens();
        $data['playhandsize'] = sizeof($this->piles->playHand);
		$data['playareasize'] = sizeof($this->piles->playArea);
        $data['drawdecksize'] = $this->obfuscateNumber(sizeof($this->piles->drawDeck));
        $data['discarddecksize'] = sizeof($this->piles->discardDeck);
        return $data;
    }

    private function obfuscateNumber($num) {
        if ($num < 5) {
            return $num;
        }
        return (($num / 5) * 5) + 2;
    }

    public function getName() {
        return $this->name;
    }

    public function getGender() {
        return $this->gender;
    }

    public function getNumberOfTurns() {
        return $this->numberOfTurns;
    }

    public function save($gid, $order) {
        $db = DBUtils::Create();
        $db->savePlayer($gid, $this->name, $order, $this->numberOfActionsPlayed, $this->numberOfTreasuresPlayed, $this->numberOfTurns, $this->numberOfCardsBought, $this->undoStack, $this->possessedTurns);

        $this->piles->save($gid, $this->name);
        $this->resources->save($gid, $this->name);
    }

    public function endTurn(Game $game) {

        //checking that we are in the correct phase
        $phase = $this->phase;

        if ($phase != Phase_Action && $phase != Phase_Buy && $phase != Phase_Payment) {
            return false;
        }
        //it is important to save the play area for future reference before it gets cleaned up!
        $playArea = $this->getPlayArea();

        $this->discardCardsFromDurationDeck();
        $this->cleanup($game);

        $this->setPhase($game, Phase_Sleep);
        MySocket::deleteInputOutput($game->getId(), $this->getName());
        $this->piles->endTurn($game);
        $this->resources->endTurn();
        $this->numberOfActionsPlayed = 0;
        $this->numberOfTreasuresPlayed = 0;
        $this->undoStack = array();

        //checking if there is an outpost in play
        $durationAffects = $this->getDurationAffects();
        $outpost_in_affect = false;
        $bad_outpost_in_affect = false;
        $outpost = new Outpost();
        $outpost_key = $outpost->getKey();

        foreach ($durationAffects as $d_card_arr) {
            $d_card = $d_card_arr['card'];
            $d_isActive = $d_card_arr['quantity'];
            if ($d_card->getKey() == $outpost_key) {
                if ($d_isActive != RETURN_DURATION_TO_DISCARD && $d_isActive != DELETE_VIRTUAL_DURATION_CARD) {
                    $outpost_in_affect = true;
                    break;
                }
            }
        }

        if ($outpost_in_affect) {
            $drawed_cards = $this->drawCards($game, 3);
            $this->numberOfTurns -= 1;
            ScreenDisplay::addScreenMessage($game, true, "clean up and draw " . nicePrint($drawed_cards), "cleans up and draws " . sizeof($drawed_cards) . " cards", $this->name);
            return OUTPOST_DONT_CHANGE_PLAYER;
        }

        foreach ($playArea as $p_card) {
            if ($p_card->getKey() == $outpost_key) {
                $bad_outpost_in_affect = true;
                break;
            }
        }

        if ($bad_outpost_in_affect) {
            $this->numberOfTurns -= 1;
            $drawed_cards = $this->drawCards($game, 3);
            ScreenDisplay::addScreenMessage($game, true, "clean up and draw " . nicePrint($drawed_cards), "cleans up and draws " . sizeof($drawed_cards) . " cards", $this->name);
        } else if ($this->possessedTurns > 0) {
            $this->possessedTurns -= 1;
            $drawed_cards = $this->drawCards($game, 5);
            ScreenDisplay::addScreenMessage($game, true, "clean up and draw " . nicePrint($drawed_cards), "cleans up and draws " . sizeof($drawed_cards) . " cards", $this->name);
            return POSSESED_TURN;
        } else {
            $drawed_cards = $this->drawCards($game, 5);
            ScreenDisplay::addScreenMessage($game, true, "clean up and draw " . nicePrint($drawed_cards), "cleans up and draws " . sizeof($drawed_cards) . " cards", $this->name);
            return true;
        }
    }

    public function startTurn(Game $game) {

        $this->numberOfTurns += 1;

        ScreenDisplay::addScreenMessage($game, true, NEW_LINE, NEW_LINE);
        ScreenDisplay::addScreenMessage($game, true, "start turn " . $this->numberOfTurns, "starts turn " . $this->numberOfTurns, null, true, false);

        $this->resources->startTurn();
        $this->piles->startTurn($game, $this->name);
        $playHand = $this->getPlayHand();
        $actiontsFound = false;
        foreach ($playHand as $card) {
            if ($card instanceof iAction) {
                $actiontsFound = true;
                break;
            }
        }

        if ($actiontsFound)
            $this->setPhase($game, Phase_Action);
        else
            $this->setPhase($game, Phase_Payment);
        $this->numberOfActionsPlayed = 0;
        $this->numberOfTreasuresPlayed = 0;

        alertPlayer($this->name);
    }

    public function action($card_name, Game $game) {
        if ($this->getPhase() != Phase_Action) {
            $this->setUniqueid();
            return array("error" => '*** wrong phase ***');
        }
        if ($this->getActions() == 0) {
            $this->setUniqueid();
            return array("error" => '*** no actions ***');
        }

        $possible_card = n2c($card_name);
        $card = $this->getCardInHand($possible_card);

        if (!$card) {
            $this->setUniqueid();
            return array("error" => '*** no such card in hand ***');
        }

        if (!$card instanceof iAction) {
            $this->setUniqueid();
            return array("error" => '*** not an action card ***');
        }

        //playing the card: remoning 1 action, removing from hand, placing on play area pushing to stack and call the helper
        $this->addActions(-1);
        $game->moveCardFromPileToPile($card, PlayHand, PlayArea);

        //screed display
        ScreenDisplay::addScreenMessage($game, true, "play " . nicePrint($card), "plays " . nicePrint($card));

        $this->pushCardToStack($card);
        $this->actionHelper($game);
    }

    public function resolveCardsStack(Game $game, $numOfCards = PHP_INT_MAX) {
        $phase = $this->getPhase();
        if ($phase == Phase_Action) {
            $this->actionHelper($game, $numOfCards);
        } else if ($phase == Phase_Payment || $phase == Phase_BlackMarketPayment || $phase == Phase_DigitalStorePayment) {
            $this->paymentHelper($game, $numOfCards);
        }
    }

    public function actionHelper(Game $game, $numOfCards = PHP_INT_MAX) {
        $counter = 0;
        while (sizeof($this->piles->cardsStack) > 0 && $counter < $numOfCards) {
            $phase = $this->getPhase();
            if ($phase != Phase_Action)
                break;
            $card_arr = array_pop($this->piles->cardsStack);

            $card = $card_arr['card'];
            $isRealCard = $card_arr['quantity'];
            $cardValidUndo = true; //$card_arr['validUndo'];
            $this->numberOfActionsPlayed = $this->numberOfActionsPlayed + 1;
            $card->play($game, $isRealCard);
            if ($isRealCard == REAL_CARD) {
                $money = $this->getMoney();
                $undoParams = array();
                $undoParams['coins'] = $money->getCoins();
                $undoParams['potions'] = $money->getPotions();
                $undoParams['actions'] = $this->getActions() + 1;
                $undoParams['buys'] = $this->getBuys();
                $undoParams['card'] = $card->getValue();
                list($validUndo, $undoParams) = $card->undoParamsPlay($undoParams);
                $this->addUndoAction(UNDO_PLAY_ACTION, $undoParams, ($validUndo & $cardValidUndo) == 1);
            }
            array_push($this->piles->playedCards, $card);
            $counter+=1;
        }
    }

    public function paymentHelper(Game $game, $numOfCards = PHP_INT_MAX) {
        $counter = 0;
        while (sizeof($this->piles->cardsStack) > 0 && $counter < $numOfCards) {
            $phase = $this->getPhase();
            if ($phase != Phase_Payment && $phase != Phase_BlackMarketPayment && $phase != Phase_DigitalStorePayment)
                break;
            $card_arr = array_pop($this->piles->cardsStack);
            $card = $card_arr['card'];
            $isRealCard = $card_arr['quantity'];
            $cardValidUndo = true; //$card_arr['validUndo'];
            if ($isRealCard == REAL_CARD) {
                $game->moveCardFromPileToPile($card, PlayHand, PlayArea);
            }
            $card->play($game, $isRealCard);
            if ($isRealCard == REAL_CARD && $this->phase == Phase_Payment) {
                $money = $this->getMoney();
                $undoParams = array();
                $undoParams['coins'] = $money->getCoins();
                $undoParams['potions'] = $money->getPotions();
                $undoParams['actions'] = $this->getActions();
                $undoParams['buys'] = $this->getBuys();
                $undoParams['card'] = $card->getValue();
                list($validUndo, $undoParams) = $card->undoParamsPlay($undoParams);
                $this->addUndoAction(UNDO_PLAY_TREASURE, $undoParams, ($validUndo & $cardValidUndo) == 1);
            }

            array_push($this->piles->playedCards, $card);
            $value = $card->getMoneyValue($game);
            $this->addMoney($value);
            $counter+=1;
        }
    }

    //minimal function that only runs the call to card
    //all other 'gain' functionalities are covered in cardGained method
    public function afterCardGained($game, $new_card) {
        $new_card->afterGainThisCard($game, $this->name);
    }

    public function cardGained($game, $new_card) {
        //play possible for traderoute
        $this->playTradeRoute($game, $new_card);

        $this->piles->addCardToGained($new_card);
        $new_card->gainThisCard($game, $this->name);

        $game->playAllReactions($this, Gain, $new_card);
    }

    public function cleanup($game) {
        $cards = $this->getPlayArea();
        foreach ($cards as $card) {
            $card->cleanup($game);
        }
    }

    private function getTreasuresSorted(Game $game, $skipCoppers = false) {

        $treasures = array();
        foreach ($this->piles->playHand as $card) {
            if ($card instanceof iTreasure) {
                $weight = $card->autoPlayNum($game);
                #if weight is negative, we dont want to auto play this card
                if ($weight < 0) {
                    continue;
                }
                if ($card instanceof Copper && $skipCoppers) {
                    continue;
                }
                if (!array_key_exists($weight, $treasures)) {

                    $treasures[$weight] = array();
                }
                $treasures[$weight][] = $card;
            }
        }
        ksort($treasures);
        return $treasures;
    }

    private function estimateTreasuresValue($game) {
        ScreenDisplay::suspendMessages();
        $est_with_coop = $this->getMoney()->getCoins();
        $est_without_coop = $est_with_coop;

        $treasures = 0;
        $banks = 0;
        foreach ($this->piles->playHand as $card) {
            if ($card instanceof iTreasure) {
                $coins = $card->getMoneyValue($game)->getCoins();
                if (!$card instanceof Copper)
                    $est_without_coop += $coins;
                $est_with_coop += $coins;
                $treasures += 1;
                if ($card instanceof Bank) {
                    $banks += 1;
                }
            }
        }

        #adding estimated banks value
        for ($i = 0; $i < $banks; $i += 1) {
            $est_without_coop += $treasures - $i;
            $est_with_coop += $treasures - $i;
        }
        ScreenDisplay::resumeMessages();
        return array($est_with_coop, $est_without_coop);
    }

    public function addUndoAction($action, $param = array(), $validUndo) {
        $num = sizeof($this->undoStack);
        $this->undoStack[$num] = array($action, $param, $validUndo);
    }

    public function buyPhaseAndPlayAllTreasures(Game $game) {

        $oldPhase = $this->getPhase();
        if ($oldPhase != Phase_Payment) {
            $undoParams = array();
            $undoParams['phase'] = $oldPhase;
            $this->addUndoAction(UNDO_BUY_PHASE, $undoParams, true);
        }
        $this->setPhase($game, Phase_Payment);

        $played_treasures = array();
        list ($est_with_coopers, $est_without_coopers) = $this->estimateTreasuresValue($game);
        $gm = new GrandMarket();
        $skip_coppers = false;
        if ($game->isCardOnBoard($gm) && $est_without_coopers >= $gm->getPrice($this)->getCoins())
            $skip_coppers = true;

        $treasures = $this->getTreasuresSorted($game, $skip_coppers);

        while (sizeof($treasures) > 0) {
            $weight_arr = array_shift($treasures);
            $card = array_shift($weight_arr);
            $this->pushCardToStack($card);
            $played_treasures[] = $card;
            $this->resolveCardsStack($game);
            $treasures = $this->getTreasuresSorted($game, $skip_coppers);
        }
        if (sizeof($played_treasures))
            ScreenDisplay::addScreenMessage($game, true, "play " . nicePrint($played_treasures), " plays " . nicePrint($played_treasures));
        //$this->paymentHelper($game);
    }

    public function getOverpayAmount(Game $game, $card_name, $allowZeroOverpay = false) {
        $coins = $this->getMoney()->getCoins();
        if ($coins > 0 | $allowZeroOverpay) {
            $options = array();
            for ($i = 0; $i <= $coins; $i++) {
                $options[] = new Option($i, "$i");
            }
            if ($allowZeroOverpay) {
                $options[] = new Option($i + 1, "None");
            }
            $output = MySocket::GetUserReply($game, Input_ChooseOne, $options, 1, 1, null, "How much you want to overpay for $card_name?");
            $overpay = $output[0];
            if ($overpay != "None") {
                ScreenDisplay::addScreenMessage($game, true, "overpay $overpay$", " overpays $overpay$");
                $game->addMoney(new Price(-1 * $overpay));
            }
            return $overpay;
        }
        return 0;
    }

    public function playCoinTokens(Game $game) {
        $oldPhase = $this->getPhase();
        if ($oldPhase != Phase_Payment) {
            $this->setUniqueid();
            return array("error" => '*** wrong phase ***');
        }
        
        $num = $this->getCoinTokens();
        $options = array();
        for ($i = 0; $i <= $num; $i++) {
            $options[] = new Option($i, "$i");
        }
        $output = MySocket::GetUserReply($game, Input_ChooseOne, $options, 1, 1, null, "How many coin tokens to play");
        $tokens = intval($output[0]);
        if ($tokens > 0) {
            $game->addMoney(new Price($tokens));
            $game->addCoinTokens(-1 * $tokens);
            ScreenDisplay::addScreenMessage($game, true, "pay $tokens coin tokens ", "pays $tokens coin tokens");
        }
        $this->setPhase($game,Phase_Payment);
    }

    public function buyPhase(Game $game) {
        $oldPhase = $this->getPhase();
        if ($oldPhase != Phase_Action) {
            $this->setUniqueid();
            return array("error" => '*** wrong phase ***');
        }

        if ($oldPhase != Phase_Payment) {
            $undoParams = array();
            $undoParams['phase'] = $oldPhase;
            $this->addUndoAction(UNDO_BUY_PHASE, $undoParams, true);
        }

        $this->setPhase($game, Phase_Payment);
    }

    public function payment($card_name, Game $game) {

        if ($this->getPhase() != Phase_Payment && $this->getPhase() != Phase_BlackMarketPayment && $this->getPhase() != Phase_DigitalStorePayment) {
            $this->setUniqueid();
            return array("error" => '*** wrong phase ***');
        }

        $possible_card = n2c($card_name);
        $card = $this->getCardInHand($possible_card);

        if (!$card) {
            $this->setUniqueid();
            return array("error" => '*** no such card in hand ***');
        }

        if (!$card instanceof iTreasure) {
            $this->setUniqueid();
            return array("error" => '*** not a treasure card ***');
        }
        ScreenDisplay::addScreenMessage($game, true, "play " . nicePrint($card), " plays " . nicePrint($card));
        $this->pushCardToStack($card);
        $this->resolveCardsStack($game);
    }

    public function buy($card_name, $game) {
        if ($this->getPhase() != Phase_Buy && $this->getPhase() != Phase_Payment && $this->getPhase() != Phase_DigitalStoreBuy) {
            $this->setUniqueid();
            return null;
        }
        if ($this->getBuys() == 0) {
            $this->setUniqueid();
            return null;
        }
        $card = $game->peekCardFromBoard($card_name);
        if (!$card) {
            $this->setUniqueid();
            return null;
        }
        $total_money = $this->getMoney();
        if ($total_money->compare($card->getPrice($game)) < 0) {
            $this->setUniqueid();
            return null;
        }
        $oldPhase = $this->getPhase();
        $this->setPhase($game, Phase_Buy);

        //really buying the card: removing 1 buy, paying for card, getting the card and placing it into discard pile

        $this->addBuys(-1);
        $price = $card->getPrice($game);
        $substract_price = new Price(-1 * $price->getCoins(), -1 * $price->getPotions());
        $this->AddMoney($substract_price);
        ScreenDisplay::addScreenMessage($game, true, "buy " . nicePrint($card), "buys " . nicePrint($card));
        $res = $card->buy($game);
        $card = $game->moveCardFromPileToPile($card, BoardCards, DiscardPile);

        $undoParams = array();
        $undoParams['phase'] = $oldPhase;
        $undoParams['card'] = $card->getValue();
        list($validUndo, $undoParams) = $card->undoParamsBuy($undoParams);
        $this->addUndoAction(UNDO_BUY_CARD, $undoParams, $validUndo);

        $this->piles->addCardToBought($card);
        $this->numberOfCardsBought += 1;
        $playArea = $this->getPlayArea();
        $game->playAllReactions($this, Buy, $card);

        $this->playEmbargo($game, $card);
        return $card;
    }

    public function buyCardFromBlackMarket($card, $game) {

        $total_money = $this->getMoney();
        if ($total_money->compare($card->getPrice($game)) < 0) {
            $this->setUniqueid();
            return false;
        }

        //really buying the card: removing 1 buy, playing for card, getting the card and placing it into discard pile
        $price = $card->getPrice($game);
        $substract_price = new Price(-1 * $price->getCoins(), -1 * $price->getPotions());
        $this->AddMoney($substract_price);
        $res = $card->buy($game);
        $card = $game->moveCardFromPileToPile($card, BlackMarketPile, DiscardPile);
        $this->piles->addCardToBought($card);
        $this->numberOfCardsBought += 1;
        $playArea = $this->getPlayArea();
        $game->playAllReactions($this, Buy, $card);

//        foreach ($playArea as $playAreaCard) {
//            $playAreaCard->afterBuy($game, $card);
//        }
        return $card;
    }

    public function addActions($actions_num) {
        $this->resources->addActions($actions_num);
        return true;
    }

    public function addBuys($buys_num) {
        $this->resources->addBuys($buys_num);
        return true;
    }

    public function addMoney($price) {
        $this->resources->addMoney($price);
        return true;
    }

    public function playEmbargo(Game $game, $card) {
        $embargoed_cards = $game->getEmbargoedCards();
        foreach ($embargoed_cards as $ec) {
            if ($card->getKey() == $ec->getKey()) {
                $curse = $game->moveCardFromPileToPile("Curse", BoardCards, DiscardPile);
                ScreenDisplay::addScreenMessage($game, true, "gain " . nicePrint($curse) . " from embargo", "gains " . ScreenDisplay::$LMSG . " from embargo");
            }
        }
    }

    public function playTradeRoute(Game $game, $card) {
        if ($card instanceof iVictory) {
            $tradeRouteCards = $game->getTradeRouteCards();
            $same_victory_found = false;
            foreach ($tradeRouteCards as $t_card) {
                if ($t_card->getKey() == $card->getKey()) {
                    $same_victory_found = true;
                    break;
                }
            }
            if (!$same_victory_found)
                $game->addCardToTradeRoute($card);
        }
    }

    public function getVictoryPoints(Game $game, $card) {
        return $card->getVictoryPoints($game, $this->getName());
    }

    public function getVictoryPointsCardsAndTokens($game) {
        $allCards = $this->getAllCards();
        $vp = 0;
        $vcards = array();
        foreach ($allCards as $card) {
            if ($card instanceof iVictory || $card instanceof iCurse) {
                $vp += $card->getVictoryPoints($game, $this->getName());
                $vcards[] = $card;
            }
        }
        $vt = $this->resources->victoryTokens;
        $vp += $vt;
        return array($vp, $vcards, $vt);
    }

    public function addPossessedTurn($name = null) {
        $this->possessedTurns += 1;
    }

    public function getVictoryTokens() {
        return $this->resources->victoryTokens;
    }

    public function drawCards($game, $num, $printMessage = true) {
        return $this->piles->drawCards($game, $this->name, $num, $printMessage);
    }

    public function peekCardFromDrawDeck($game) {
        return $this->piles->peekCardFromDrawDeck($game, $this->name);
    }

    public function removeCardsFromDrawDeck($game, $card_num) {
        return $this->piles->removeCardsFromDrawDeck($game, $this->name, $card_num);
    }

    public function removeBottomDrawDeckCard($game) {
        return $this->piles->removeBottomDrawDeckCard($game, $this->name);
    }

    public function putCardOnBottomOfDrawDeck($card) {
        return $this->piles->putCardOnBottomOfDrawDeck($card);
    }

    public function discardPlayHand() {
        return $this->piles->discardPlayHand();
    }

    public function discardCardsFromDrawDeck($game, $num) {
        return $this->piles->discardCardsFromDrawDeck($game, $this->name, $num);
    }

    public function discardCardsFromDurationDeck() {
        return $this->piles->discardCardsFromDurationDeck();
    }

    public function discardTempDiscardCards($discardCards) {
        return $this->piles->discardTempDiscardCards($discardCards);
    }

    public function putCardsInPlayHand($cards) {
        $this->piles->putCardsInPlayHand($cards);
        return $cards;
    }

    public function putCardInPlayHand($card) {
        return $this->piles->putCardInPlayHand($card);
    }

    public function getCardInHand($card) {
        return $this->piles->getCardInHand($card);
    }

    public function putCardInPlayArea($card) {
        array_unshift($this->piles->playArea, $card);
        return $card;
    }

    public function putCardOnTopOfDrawDeck($card) {
        array_unshift($this->piles->drawDeck, $card);
        return $card;
    }

    public function putCardInDiscardDeck($card) {
        array_unshift($this->piles->discardDeck, $card);
        return $card;
    }

    public function getPlayHand() {
        return $this->piles->playHand;
    }

    public function getDiscardDeck() {
        return $this->piles->discardDeck;
    }

    public function getPlayArea() {
        return $this->piles->getPlayArea();
    }

    public function getPlayedCards() {
        return $this->piles->playedCards;
    }

    public function getGainedCards() {
        return $this->piles->gainedCards;
    }

    public function getBoughtCards() {
        return $this->piles->boughtCards;
    }

    public function getContrabandCards() {
        return $this->piles->contrabandCards;
    }

    public function contrabandCard($card) {
        return $this->piles->contrabandCard($card);
    }

    public function removeCardFromPlayHand($card) {
        $card = $this->piles->removeCardFromPlayHand($card);
        return $card;
    }

    public function removeCardFromPlayArea($card) {
        $card = $this->piles->removeCardFromPlayArea($card);
        return $card;
    }

    public function removeCardFromDiscardPile($card) {
        $card = $this->piles->removeCardFromDiscardPile($card);
        return $card;
    }

    public function putCardInIslandDeck($card) {
        return $this->piles->putCardInIslandDeck($card);
    }

    public function putCardInHavenDeck($card) {
        return $this->piles->putCardInHavenDeck($card);
    }

    public function putCardInDurationDeck($card) {
        //we here only remove the card from the play area, since the duration handling is done by the card itself
        $this->removeCardFromPlayArea($card);
    }

    public function PutCardsFromHavenDeckToHand() {
        return $this->piles->PutCardsFromHavenDeckToHand();
    }

    public function putCardInNativeVillageDeck($card) {
        return $this->piles->putCardInNativeVillageDeck($card);
    }

    public function putCardInHorseTradersDeck($card) {
        return $this->piles->putCardInHorseTradersDeck($card);
    }

    public function getTurnNumber() {
        return $this->numberOfTurns;
    }

    public function PutCardsFromNativeVillageDeckToHand() {
        return $this->piles->PutCardsFromNativeVillageDeckToHand();
    }

    public function getPhase() {
        //KIRIL: changes getphase to access the most relevant db data. part of the fix of loops in mysocket (that maybe sent players to sleep by mistake)
        $db = DBUtils::Create();
        $name = $this->getName();
        $phase = $db->getPhase($name);
        $this->phase = $phase;
        return $phase;
        //return $this->phase;
    }

    public function getRoom() {
        return $this->room;
    }

    public function setPhase(Game $game, $phase) {
        $this->phase = $phase;
        $gid = $game->getId();
        $name = $this->getName();
        $db = DBUtils::Create();
        $db->setPhase($gid, $name, $phase);
        return true;
    }

    public function getTotalCardsNum() {
        $allCards = $this->getAllCards();
        return sizeof($allCards);
    }

    public function getAllCards() {
        return $this->piles->getAllCards();
    }

    public function getPlayHandSize() {
        return sizeof($this->piles->playHand);
    }

    public function getDrawDeckSize() {
        return sizeof($this->piles->drawDeck);
    }

    public function getDrawDeck() {
        return $this->piles->drawDeck;
    }

    public function getDiscardDeckSize() {
        return sizeof($this->piles->discardDeck);
    }

    public function getDurationAffects() {
        return $this->piles->durationAffects;
    }

    public function getRevealedReactions() {
        return $this->piles->reactions;
    }

    public function pushCardToStack($card, $isRealCard = 1, $validUndo = true) {
        return array_push($this->piles->cardsStack, array('card' => $card, 'quantity' => $isRealCard, 'validUndo' => $validUndo));
    }

    public function pushCardToReactions($card) {
        return array_push($this->piles->reactions, $card);
    }

    public function pushCardToDurationAffects($card, $quantity, $isRealCard) {
        $this->piles->pushCardToDurationAffects($card, $quantity, $isRealCard);
    }

    public function pushCardToInfluence($card) {
        return array_push($this->piles->cardsInfluence, $card);
    }

    public function getActions() {
        return $this->resources->actions;
    }

    public function getBuys() {
        return $this->resources->buys;
    }

    public function getMoney() {
        return $this->resources->getMoney();
    }

    public function putDrawPileInDiscard() {
        $this->piles->discardDeck = array_merge($this->piles->drawDeck, $this->piles->discardDeck);
        $this->piles->drawDeck = array();
    }

    public function getNumberOfActionsPlayed() {
        return $this->numberOfActionsPlayed;
    }

    public function getNumberOfActionsInPlay() {
        $actions = 0;
        foreach ($this->piles->playArea as $card) {
            if ($card instanceof iAction) {
                $actions++;
            }
        }
        return $actions;
    }

    public function runInfluenceCards(Game $game) {
        foreach ($this->piles->cardsInfluence as $card) {
            $card->influence($game);
        }
    }

    public function getNumberOfTreasuresPlayed() {
        return $this->numberOfTreasuresPlayed;
    }

    public function addVictoryTokens($tokens) {
        return $this->resources->addVictoryTokens($tokens);
    }

    public function addCoinTokens($tokens) {
        return $this->resources->addCoinTokens($tokens);
    }

    public function getPirateShipTokens() {
        return $this->resources->getPirateShipTokens();
    }

    public function getCoinTokens() {
        return $this->resources->getCoinTokens();
    }

    public function addPirateShipToken() {
        return $this->resources->addPirateShipToken();
    }

    public function undo(Game $game) {
        if (sizeof($this->undoStack) > 0) {
            $num = sizeof($this->undoStack) - 1;
            $undoItem = $this->undoStack[$num];
            $action = $undoItem[0];
            $params = $undoItem[1];
            $validUndo = $undoItem[2];

            $undoed = false;
            if (!$validUndo) {
                ScreenDisplay::addScreenMessage($game, true, "can't undo last action ");
            } else {
                if ($action == UNDO_PLAY_TREASURE) {
                    $this->resources->setMoney(new Price($params['coins'], $params['potions']));
                    $this->resources->actions = $params['actions'];
                    $this->resources->buys = $params['buys'];

                    $card = n2c($params['card']);
                    $card = $game->undo_func_moveCardFromPileToPile($card, PlayArea, PlayHand);
                    ScreenDisplay::addScreenMessage($game, true, "undo 'Play Treasure' on " . nicePrint($card), "undos 'Play Treasure' on " . ScreenDisplay::$LMSG);
                    $this->piles->removeCardFromPlayedCards($card);
                    $undoed = true;
                } elseif ($action == UNDO_PLAY_ACTION) {
                    $undoed = true;
                    $this->resources->setMoney(new Price($params['coins'], $params['potions']));
                    $this->resources->actions = $params['actions'];
                    $this->resources->buys = $params['buys'];

                    $card = n2c($params['card']);
                    $actionPile = PlayArea;
                    if (isset($params['actionPile']))
                        $actionPile = $params['actionPile'];
                    $card = $game->undo_func_moveCardFromPileToPile($card, $actionPile, PlayHand);
                    ScreenDisplay::addScreenMessage($game, true, "undo 'Play Action' on " . nicePrint($card), "undos 'Play Action' on " . ScreenDisplay::$LMSG);
                    $card->undo($game, $params);
                    $this->piles->removeCardFromPlayedCards($card);
                    $undoed = true;
                } elseif ($action == UNDO_BUY_CARD) {
                    $phase = $params['phase'];
                    $card = n2c($params['card']);
                    $card = $game->undo_func_moveCardFromPileToPile($card, DiscardPile, BoardCards);
                    $this->addBuys(1);
                    $this->addMoney($card->getPrice($game));
                    $this->setPhase($game, $phase);
                    ScreenDisplay::addScreenMessage($game, true, "undo 'Buy Card' on " . nicePrint($card), "undos 'Buy Card' on " . ScreenDisplay::$LMSG);
                    $undoed = true;
                } elseif ($action == UNDO_BUY_PHASE) {
                    $phase = $params['phase'];
                    ScreenDisplay::addScreenMessage($game, true, "undo 'Buy Phase' and return to 'Action Phase'", "undos 'Buy Phase' and returns to 'Action Phase'");
                    $this->setPhase($game, $phase);
                    $undoed = true;
                }
            }

            if ($undoed)
                unset($this->undoStack[$num]);
        }
    }

    public function shuffleDrawDeck() {
        $this->piles->shuffleDrawDeck();
    }

    public function screenAppend($isNewLine, $msg) {
        if ($isNewLine)
            $msg = "|" . $msg;
        $this->screen .= $msg;
    }

    public function isResigned(Game $game) {

        $gameID = $game->getId();
        $phase = $this->getPhase();
        $playerRoom = $this->getRoom();
        if ($phase == Phase_GameResign || $gameID != $playerRoom)
            return true;
        return false;
    }

    public function getCardFromPile($card, $srcPile) {
        switch ($srcPile) {
            case PlayArea:
                return $this->removeCardFromPlayArea($card);
            case PlayHand:
                return $this->removeCardFromPlayHand($card);
            case DiscardPile:
                return $this->removeCardFromDiscardPile($card);
            default:
                throw new Exception("Not a valid pile " . $srcPile);
        }
    }

    public function setUniqueid() {
        $db = DBUtils::Create();
        $db->setUniqueid($this->name);
    }

    public function putCardToPile($card, $dstPile) {
        switch ($dstPile) {
            case DiscardPile:
                return $this->putCardInDiscardDeck($card);
            case DrawPile:
                return $this->putCardOnTopOfDrawDeck($card);
            case PlayArea:
                return $this->putCardInPlayArea($card);
            case PlayHand:
                return $this->putCardInPlayHand($card);
            case IslandPile:
                return $this->putCardInIslandDeck($card);
            case NativeVillagePile:
                return $this->putCardInNativeVillageDeck($card);
            case HorseTradersPile:
                return $this->putCardInHorseTradersDeck($card);
            case HavenPile:
                return $this->putCardInHavenDeck($card);
            case DurationPileCards:
                return $this->putCardInDurationDeck($card);
            default:
                throw new Exception("Not a valid pile " . $dstPile);
        }
    }

}

?>